package com.star.common.oss.service.impl;

import com.google.common.collect.Maps;
import com.obs.services.ObsClient;
import com.obs.services.model.*;
import com.star.common.oss.exception.OssException;
import com.star.common.oss.model.FileUploadStatusVO;
import com.star.common.oss.model.OssStsVO;
import com.star.common.oss.model.OssTemporaryUrlVO;
import com.star.common.oss.properties.OssProperties;
import com.star.common.oss.service.AbstractOssClient;
import com.star.common.oss.utils.StrPoolUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.IOUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;

/**
 * 华为云oss处理类
 *
 * @author zhouhengzhe #公众号：小白的Java进阶之路
 * @version 1.0
 * @date 2023/6/27 14:08
 */
@Slf4j
public class HuaweiCloudOssClient extends AbstractOssClient {

    private final ObsClient obsClient;

    public HuaweiCloudOssClient(OssProperties.HuaweiCloudOssProperties properties) {
        super.checkAllPropertiesNotNull(properties);
        obsClient = new ObsClient(properties.getAk(), properties.getSk(), properties.getEndPoint());
    }


    /**
     * 获取临时访问凭证
     *
     * @param sessionName   SessionName的长度和规则，不要有空格，只能有'-' '_' 字母和数字等字符
     * @param bucketName    oss桶 例如 ldstore
     * @param regionId      RAM的地域ID。以华东1（杭州）地域为例，regionID填写为cn-hangzhou。也可以保留默认值，默认值为空字符串（""）。
     * @param stsApiVersion sts版本
     * @param roleArn       获取的角色ARN
     * @param policyJson    比如以下Policy用于限制仅允许使用临时访问凭证向目标存储空间examplebucket下的src目录上传文件。
     *                      临时访问凭证最后获得的权限是步骤4设置的角色权限和该Policy设置权限的交集，即仅允许将文件上传至目标存储空间examplebucket下的src目录。
     *                      如果policy为空，则用户将获得该角色下所有权限。
     *                      {\n" +
     *                      "    \"Version\": \"1\", \n" +
     *                      "    \"Statement\": [\n" +
     *                      "        {\n" +
     *                      "            \"Action\": [\n" +
     *                      "                \"oss:PutObject\"\n" +
     *                      "            ], \n" +
     *                      "            \"Resource\": [\n" +
     *                      "                \"acs:oss:*:*:examplebucket/src/*\" \n" +
     *                      "            ], \n" +
     *                      "            \"Effect\": \"Allow\"\n" +
     *                      "        }\n" +
     *                      "    ]\n" +
     *                      "}
     * @return 临时访问凭证
     */
    @Override
    public OssStsVO getSts(String sessionName,
                           String bucketName,
                           String regionId,
                           String stsApiVersion,
                           String roleArn,
                           String policyJson) {
        throw new OssException("Features are not under development get sts by huaweiyun");
    }

    /**
     * 获取临时url
     *
     * @param contentType  请求头
     * @param bucketName   桶名
     * @param objectKey    文件名
     * @param documentType 文件类型 例如 jpg
     * @return
     */
    @Override
    public OssTemporaryUrlVO getTemporaryUrl(String contentType, String bucketName, String objectKey, String documentType) {
        checkProperties(contentType, bucketName, objectKey, documentType);
        String objectName = UUID.randomUUID() + "." + documentType;
        // URL有效期，3600秒
        long expireSeconds = 3600L;

        Map<String, String> headers = Maps.newHashMap();
        headers.put(StrPoolUtils.CONTENT_TYPE, contentType);

        TemporarySignatureRequest request = new TemporarySignatureRequest(HttpMethodEnum.PUT, expireSeconds);
        request.setBucketName(bucketName);
        request.setObjectKey(objectKey + objectName);
        request.setHeaders(headers);

        TemporarySignatureResponse response = obsClient.createTemporarySignature(request);
        OssTemporaryUrlVO ossTemporaryUrlVO = new OssTemporaryUrlVO();
        ossTemporaryUrlVO.setUrl(response.getSignedUrl());
        ossTemporaryUrlVO.setObjectName(objectKey + objectName);

        return ossTemporaryUrlVO;
    }

    /**
     * 文件上传(<a href="https://support.huaweicloud.com/sdk-java-devg-obs/obs_21_0603.html">...</a>)
     *
     * @param uploadFile 上传的文件
     * @param fileName   文件名称
     * @param bucketName 桶名称
     * @return 返回的路径
     */
    @Override
    public String uploadFile(MultipartFile uploadFile, String fileName, String bucketName) {
        try {
            // 判断桶是否存在
            boolean exists = obsClient.headBucket(bucketName);
            if (!exists) {
                // 若不存在，则创建桶
                HeaderResponse headerResponse = obsClient.createBucket(bucketName);
                log.info("create bucketName succeed，requestId is`{}", headerResponse.getRequestId());
            }
            //获取文件信息
            InputStream inputStream = uploadFile.getInputStream();
            long available = inputStream.available();
            PutObjectRequest request = new PutObjectRequest(bucketName, String.valueOf(fileName), inputStream);
            ObjectMetadata objectMetadata = new ObjectMetadata();
            objectMetadata.setContentLength(available);
            request.setMetadata(objectMetadata);
            //设置公共读
            request.setAcl(AccessControlList.REST_CANNED_PUBLIC_READ);
            PutObjectResult putObjectResult = obsClient.putObject(request);
            if (Objects.isNull(putObjectResult)) {
                throw new OssException("upload file fail ，the response is null");
            }
            // 读取该已上传对象的URL
            log.info("URL of the uploaded object is`{}", putObjectResult.getObjectUrl());
            return putObjectResult.getObjectUrl();
        } catch (IOException e) {
            log.error("call the upload url is fail the exception is`{}", e.getMessage(), e);
            throw new OssException("call the upload url is fail", e);
        }
    }

    /**
     * 文件流上传(<a href="https://support.huaweicloud.com/sdk-java-devg-obs/obs_21_0602.html">...</a>)
     *
     * @param fileName    文件名称
     * @param bucketName  桶名称
     * @param out         文件流
     * @param contentType 文件类型
     * @return 返回的路径
     */
    @Override
    public String uploadFileByte(String fileName, String bucketName, byte[] out, String contentType) {
        // 判断桶是否存在
        boolean exists = obsClient.headBucket(bucketName);
        if (!exists) {
            // 若不存在，则创建桶
            HeaderResponse headerResponse = obsClient.createBucket(bucketName);
            log.info("create bucket succeed，the requestId is`{}", headerResponse.getRequestId());
        }
        PutObjectResult putObjectResult = obsClient.putObject(bucketName, fileName, new ByteArrayInputStream(out));
        if (Objects.isNull(putObjectResult)) {
            throw new OssException("upload file fail ，the response is null");
        }
        return putObjectResult.getObjectUrl();
    }

    /**
     * 获取文件上传进度
     *
     * @param fileName   文件名称
     * @param bucketName 桶名称
     * @return 返回文件上传进度
     */
    @Override
    public FileUploadStatusVO getFileUploadPlan(String fileName, String bucketName) {
        FileUploadStatusVO fileUploadStatus = new FileUploadStatusVO();
        try {
            GetObjectRequest request = new GetObjectRequest(bucketName, fileName);
            request.setProgressListener(new ProgressListener() {
                @Override
                public void progressChanged(ProgressStatus status) {
                    //上传的平均速度
                    fileUploadStatus.setAvgSpeed(status.getAverageSpeed());
                    //上传的百分比
                    fileUploadStatus.setPct(String.valueOf(status.getTransferPercentage()));
                }
            });
            // 每下载1MB数据反馈下载进度
            request.setProgressInterval(1024 * 1024L);
            ObsObject obsObject = obsClient.getObject(request);
            // 读取对象内容
            InputStream input = obsObject.getObjectContent();
            byte[] b = new byte[1024];
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            int len;
            while ((len = input.read(b)) != -1) {
                //将每一次的数据写入缓冲区
                byteArrayOutputStream.write(b, 0, len);
            }
            byteArrayOutputStream.close();
            input.close();
            return fileUploadStatus;
        } catch (Exception e) {
            throw new OssException("Failed to get file upload progress", e);
        }
    }

    /**
     * 文件下载
     *
     * @param response   响应
     * @param fileName   文件名称
     * @param bucketName 桶名称
     */
    @Override
    public void fileDownload(HttpServletResponse response, String fileName, String bucketName) {
        try {
            ObsObject obsObject = obsClient.getObject(bucketName, fileName);
            InputStream input = obsObject.getObjectContent();
            //缓冲文件输出流
            BufferedOutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
            //设置让浏览器弹出下载提示框，而不是直接在浏览器中打开
            response.addHeader("Content-Disposition", "attachment;filename=" + fileName);
            IOUtils.copy(input, outputStream);
            outputStream.flush();
            outputStream.close();
            input.close();
        } catch (Exception e) {
            throw new OssException("download file fail", e);
        }
    }

    /**
     * 删除文件
     *
     * @param fileName   文件名称
     * @param bucketName 桶名称
     */
    @Override
    public void deleteFile(String fileName, String bucketName) {
        try {
            obsClient.deleteObject(bucketName, fileName);
        } catch (Exception e) {
            throw new OssException("delete file fail", e);
        }
    }
}
